import base
import os
import wsgi_compat
import httpbase
import rete
import websocket

class HttpUtils:
	versions = ["HTTP/1.1","HTTP/1.0"]
	myVersion = "HTTP/1.1"
	readContentMethods = ["POST"]
	max_post_size = 1024 * 1024
	
	@classmethod
	def parse(zelf, hRows):
		headers = list()
		base.assert1(len(hRows)>0)
		request = hRows[0].decode("UTF-8").split()
		base.assert1(len(request) == 3)
		if request[2] not in zelf.versions:
			raise base.UnsupportedProtocol(request[2])
		for row in hRows[1:]:
			decoded = row.decode("UTF-8")
			parts = decoded.partition(":")
			t = parts[0], parts[2].strip()
			headers.append(t)
		h = HeaderSection(request,headers)
		return h.method.upper() in zelf.readContentMethods, h
	
	
	errors = {
		100 : "Continue",
		101 : "Switching Protocols",
		200 : "OK",
		201 : "Created",
		202 : "Accepted",
		203 : "Non-Authoritative Information",
		204 : "No Content",
		205 : "Reset Content",
		206 : "Partial Content",
		300 : "Multiple Choices",
		301 : "Moved Permanently",
		302 : "Found",
		303 : "See Other",
		304 : "Not Modified",
		305 : "Use Proxy",
		307 : "Temporary Redirect",
		400 : "Bad Request",
		401 : "Unauthorized",
		402 : "Payment Required",
		403 : "Forbidden",
		404 : "Not Found",
		405 : "Method Not Allowed",
		406 : "Not Acceptable",
		407 : "Proxy Authentication Required",
		408 : "Request Time-out",
		409 : "Conflict",
		410 : "Gone",
		411 : "Length Required",
		412 : "Precondition Failed",
		.07 : "Bond James Bond",
		413 : "Request Entity Too Large",
		414 : "Request-URI Too Large",
		415 : "Unsupported Media Type",
		416 : "Requested range not satisfiable",
		417 : "Expectation Failed",
		500 : "Internal Server Error",
		501 : "Not Implemented",
		502 : "Bad Gateway",
		503 : "Service Unavailable",
		504 : "Gateway Time-out",
		505 : "HTTP Version not supported"
	}
	@staticmethod
	def headerizza(array):
		return "\r\n".join(str(p[0])+": "+p[1] for p in array) + "\r\n\r\n"



class Richiesta:
	gestita = False
	keepAlive = False
	headers = []
	contenuto = None
	servHTTPVersion = HttpUtils.myVersion
	def __init__(self,connessione):
		self.connessione = connessione
	def log(self,text,end="\n"):
		print(str(self.connessione.fileno())+": "+text,end=end)

def recv(recvdata,req,staticData,queue= None):
	if staticData is None:
		data = recvdata
		haveHeaders = readContent = False 
		contenuto = None
	else:
		prevData, haveHeaders, contenuto, lenC, readContent, headers, headerEnd = staticData
		data = prevData + recvdata
	#print(data)
	
	if not haveHeaders:
		headerEnd = data.find(base.aCapo + base.aCapo)
		if headerEnd != -1:
			#parse headers
			req.headers= []
			req.contenuto = None
			req.keepAlive = False
			req.gestita = False
			hRows = data [0:headerEnd].split(base.aCapo)
			try:
				readContent, headers = HttpUtils.parse(hRows)
				req.log(headers.method +" "+ headers.resource+" "+headers.version,end="")
				haveHeaders = True
				headerEnd += len(base.aCapo+base.aCapo)
			except base.UnsupportedProtocol:
				httpbase.erroreHTTP(req,505)
				return (False, None, bytes())
			except base.Assertion1Error as e:
				print("errore di asserzione")
				print (e)
				return (False, None, bytes())
		else:
			return (recv, (data,haveHeaders,None,0,False,None,headerEnd),bytes())
	if haveHeaders:
		req.headers = headers
		sockettitudine = websocket.checkSocket(headers)
		if readContent:
			lenC = int(headers.get("content-length"))
			if contenuto is None:
				#iniziamo a leggere il contenuto, buttiamo gli header
				data = data[headerEnd:]
				contenuto = bytes()
			contenuto += data
			ricevuto = len(contenuto)
			if ricevuto >= lenC:
				#potrebbe essesserci qualcosa della prossima richiesta
				#(HTTP Pipelining)
				datiAvanzati = contenuto[lenC:]
				contenuto = contenuto[0:lenC]
			elif ricevuto < lenC:
				#non ci saranno dati avanzati
				return (recv, (bytes(),haveHeaders,contenuto,lenC,readContent,headers,headerEnd),bytes())
			req.contenuto = contenuto
		else:
			req.contenuto = None
			datiAvanzati = data[headerEnd:]
		keepAlive = headers.get("connection") == "keep-alive"
		print("; keep-alive: "+str(keepAlive))
		#if len(datiAvanzati) != 0:
		#	print("<"+datiAvanzati.decode("UTF-8")+">")
		
		#gestione
		if not sockettitudine:
			req.gestore = httpbase.gestisciHTTP
			method = recv		#<-- a chi passiamo i dati ricevuti?
		else:
			#la prima volta spariamo gestisciHandshakeWebSocket
			req.gestore = websocket.gestisciHandshakeWebSocket
			#d'ora in poi i dati saran passati a parseMessaggioWS
			method = websocket.parseMessaggioWS
			keepAlive = True
		req.keepAlive = keepAlive
		if queue is None:
			gestisci(req)
		else:
			queue.put(req)
		return (method if keepAlive else False, None, datiAvanzati)
	return (False,None,bytes())


class HeaderSection:
	def __init__(self, request, headers):
		self.method = request[0]
		parti = request[1].split("?")
		#print(parti)
		self.resource = parti[0]
		self.queryString = parti[1] if len(parti)>1 else ""
		self.version = request[2]
		self.headers = headers
		hostName = self.get("Host")
		tocchi = hostName.partition(":")
		self.hostName = base.alnum_and_chars(tocchi[0],["-","_","."])
		self.port = tocchi[2] if 2 in tocchi else 80
	def get(self, headerName):
		lower = headerName.lower()
		for tupl in self.headers:
			if tupl[0].lower() == lower:
				return tupl[1]
		return ""

def mandaHeaders( req, httpStatus, headers):
	global tipoR, versioneHTTP
	if req.gestita:
		return
	if isinstance(httpStatus,int) or isinstance(httpStatus,float):
		tipo = HttpUtils.errors[httpStatus] if httpStatus in HttpUtils.errors else "Unknown error"
		eccetera = str(httpStatus) + " " + tipo
	else:
		eccetera = httpStatus
	req.fornitoContentLength= True
	if 'Content-Length' not in headers:
		req.fornitoContentLength= False
	intestaz = HttpUtils.myVersion + " " + eccetera + "\r\n"
	rete.scrivi(req.connessione, bytes(intestaz,encoding="UTF-8"), len(intestaz))
	req.log("\t" + intestaz)
	#lenContenuto = len(contenuto) if lenConenuto != -1 else lenContenuto
	h = HttpUtils.headerizza(headers)
	rete.scrivi(req.connessione, bytes(h,encoding="UTF-8"), len(h))

def gestisci(req):
	req.gestore(req)
	if not req.keepAlive:
		rete.chiudi(req.connessione)
def threadGestore(queue):
	while True:
		req = queue.get()
		gestisci(req)
